/*
	wrapping the zlib with iostream using gzFile
	setbuf NYI
	Jean-Louis Ardoint 1996
*/
#include <iostream.h>
#include <zlib.h>


using namespace std;

#ifdef  _SKIPZIP
typedef ifstream izipstream;
typedef ofstream ozipstream;
#else

#ifndef _ZIPIOSTREAM
#define _ZIPIOSTREAM


#define zapeof(c) ((unsigned char)(c))


// KJD HACK for /hgsc/gnu/bin/gcc
// These functions were there in 2.95, but gone in 3.l
//int allocate() { // For AT&T compatibility
//  if (base() || unbuffered()) return 0;
//  else return doallocate(); 
//}
//char* base() const { return _IO_buf_base; }

// END KJD HACK. 

//bool bzipout = true;

//----------------------------------------------------------------------------------------
// * zipstreambuf
//----------------------------------------------------------------------------------------
//
class zipstreambuf : public streambuf
{
public:
  static const int openprot;
  private:
  char opened;
  int mode;
  gzFile mgzFile; // Gzipped file. 
  //File mFile;     // Plain file
public:
  zipstreambuf() { opened = 0; }
  int is_open() { return opened; }
  
  
  //-------------------------------------------
  // * open
  //-------------------------------------------
  //
  zipstreambuf* open(const char* name, int om)
  {
	 if (is_open())
		return (zipstreambuf*)0;
	 
	 mode = om;
	 // no append nor read/write mode
	 if ((mode & ios::ate) || (mode & ios::app)
		  || ((mode & ios::in) && (mode & ios::out)))
		return (zipstreambuf*)0;
	 
	 char fmode[10];
	 char * fmodeptr = fmode;
	 if (mode & ios::in){
		*fmodeptr++ = 'r';
	 }else if (mode & ios::out){
		*fmodeptr++ = 'w';
	 }
	 *fmodeptr++ = 'b';
	 *fmodeptr = '\0';
	 
	 // KJD use a regular stream if the bzipout flag isn't set. 


	 mgzFile = gzopen(name,fmode);
	 if (mgzFile == NULL)
		return (zipstreambuf*)0;
	 
	 opened = 1;
	 return this;
  }

  //-------------------------------------------
  // * close
  //-------------------------------------------
  //
  zipstreambuf * close()
  {
	 if (opened)
		{
		  sync();
		  opened = 0;
		  if (gzclose(mgzFile) == Z_OK)
			 return this;
		}
	 return (zipstreambuf*)0;
  }

	~zipstreambuf() { close(); }

  //-------------------------------------------
  // * overflow
  //-------------------------------------------
  //
  virtual int zipstreambuf::overflow(int c=EOF)
  {
	
	 if (!(mode&ios::out) || !opened || allocate()==EOF) return EOF;
	 if (gptr() && egptr() > gptr())
		return EOF;
	 
	 setg(0,0,0);
	 
	 // make sure there is a put area
	 if (!pptr()) setp(base(),base());
	 
	 int w = pptr() -pbase();
	 
	 if (c != EOF)
		{
		  *pptr() = c;
		  ++w;
		}
	 
	 if (gzwrite(mgzFile,pbase(),w))
		{
		  setp(base(),ebuf()-1);
		  return zapeof(c);
		}
	 
	 setp(0,0);
	 return EOF;
  }

  //-------------------------------------------
  // * underflow
  //-------------------------------------------
  //
  virtual int underflow()
  {
	 if (gptr() && gptr() < egptr()) return zapeof(*gptr());
	 
	 if (!(mode&ios::in) || !opened || allocate()==EOF) return EOF;
	 
	 int count = gzread(mgzFile,base(),blen());
	 if (count < 1)
		{
		  setg(0,0,0);
		  return EOF;
		}
	 setg(base(),base(),base()+count);
	 return zapeof(*base());
  }

  //-------------------------------------------
  // * sync
  //-------------------------------------------
  //
  virtual int sync()
  {
	 if (gptr() && egptr() > gptr())
		return EOF;
	 if (pptr() && pptr() > pbase())
		return overflow(EOF);
	 
	 return 0;
  }

/* NYI
	virtual streambuf*
			setbuf(char*  p, int len) ;
*/
};


//----------------------------------------------------------------------------------------
// * zipstreambase
//----------------------------------------------------------------------------------------
//
class zipstreambase : virtual public ios
{
	zipstreambuf buf;
public:
	zipstreambase() {init(&buf);}
	
// NYI	void setbuf(char*  p, int l);
	zipstreambuf* rdbuf() { return &buf; }


  zipstreambase(const char* name, int mode)
  {
	 init(&buf);
	 open(name,mode);
  }
  
  ~zipstreambase()
  {
	 if(buf.is_open())
		buf.close();
  }
  
  void open(const char* name, int mode)
  {
	 if (!buf.open(name,mode))
		clear(rdstate() & ios::badbit);
  }
  void close()
  {
	 if (buf.is_open())
		if (!buf.close())
		  clear(rdstate() & ios::badbit);
  }

};

//----------------------------------------------------------------------------------------
// * izipstream
//----------------------------------------------------------------------------------------
//
class izipstream : public zipstreambase, public istream
{
public:
  izipstream() {}
  izipstream(const char* name,
				 int mode=ios::in)
	 : zipstreambase(name,mode) {}
  
  zipstreambuf* rdbuf() {return zipstreambase::rdbuf(); }
  void open(const char* name)
  {zipstreambase::open(name,ios::in);}
};

//----------------------------------------------------------------------------------------
// * ozipstream
//----------------------------------------------------------------------------------------
//
class ozipstream : public zipstreambase, public ostream
{
public:
	ozipstream() {}
	ozipstream(const char* name,
                        int mode=ios::out)
		: zipstreambase(name,mode) {}

	zipstreambuf* rdbuf() {return zipstreambase::rdbuf(); }
	void open(const char* name)
		{zipstreambase::open(name,ios::out);}
};



#endif
#endif 
